#include "VoiceRecognizer.h"
#include <windows.h>
#include <mmsystem.h>
#include <iostream>
#include <string>
#include <fstream>
#include <pocketsphinx.h>
#include "Recorder.h"
#include "SoundPlayer.h"


_declspec(dllimport) extern "C" FILE * err_set_logfp(FILE *logfp);


void VoiceRecognizer::appendBuffer(SoundBuffer & sb)
{
	synchronized (this)
	{
		buffers.push(&sb);
	}
}



bool VoiceRecognizer::exceptionHandler(void * address, int code)
{
	Beep (500, 200);
	return true;
}

int VoiceRecognizer::run()
{
	try
	{
		recognitionLoop();
	}
	catch (const char * s)
	{
		MessageBox(0,s,s,0);
	}

	std::cout << "Voice Recognizer is finishing!" << std::endl;
	return 0;
}

int VoiceRecognizer::recognitionLoop()
{
	/////////////////////////
	int sampleRate = 16000;
	size_t nsamp = milliseconds * sampleRate / 1000;
	int rv;
	char const * hyp = NULL, *uttid;
	int32 score;
	std::string last_word;


	std::ifstream ifs(configFileName.c_str());

	std::string hmm;
	std::getline(ifs, hmm);

	std::string lm;
	std::getline(ifs, lm);

	std::string dict;
	std::getline(ifs, dict);

	ifs.close();


	cmd_ln_t * config;
	config = cmd_ln_init(NULL, ps_args(), TRUE,
			     "-hmm", hmm.c_str(),
			     //"-lm", (modelDir + "/lm/en/turtle.DMP").c_str(),
			     //"-dict", (modelDir + "/lm/en/turtle.dic").c_str(),
			     "-lm", lm.c_str(),
			     "-dict", dict.c_str(),
			     NULL);
	if (config == NULL)	throw "Could not create initializing parameters";

	ps_decoder_t * ps;
	ps = ps_init(config);
	if (ps == NULL)	throw "Could not initialize";

	// Suprime saida na tela
	err_set_logfp(NULL);


	double delayAccum = 0.0;
	int numBufProcessed = 0;

	rv = ps_start_utt(ps, "goforward");
	if (rv < 0) throw "Could not start utterance";

	std::cout << "Recognizer is ready!" << std::endl;

	int counter = 0;
	while (!mustTerminate())
	{
		SoundBuffer * psb = NULL;
		synchronized (this)	// critical section for buffers queue
		{
			if (!buffers.empty())	// try to get a buffer pointer
			{
				psb = buffers.front();
				buffers.pop();
			}
		}

		if (psb)	// if there is a buffer ready for processing
		{
			SoundBuffer & sb = *psb;
			short int * buffer = (short int *) sb.WaveInHdr.lpData;

			// aumenta prioridade da thread
			//int oldPriority = GetThreadPriority(GetCurrentThread());
			//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

			//std::cout << std::hex << "buffer=" << buffer << std::endl;
			rv = ps_process_raw(ps, buffer, nsamp, FALSE, FALSE);

			// Restaura prioridade da thread
			//SetThreadPriority(GetCurrentThread(), oldPriority);

#if 1
			ps_seg_t * iter = ps_seg_iter(ps, &score);
			bool wasWordIdentified = false, wasSilIdentified = false;
			std::string identifiedWord = "";
			while (iter)
			{
				std::string word = ps_seg_word(iter);
				//std::cout << word << " ";

				if (word[0] != '<'  &&  word[0] != '+')
				{
					identifiedWord = word;
					wasWordIdentified = true;
						wasSilIdentified = true;
				}

				if (wasWordIdentified && wasSilIdentified /*&& word=="</s>"*/)
				{
					rv = ps_end_utt(ps);
					if (rv < 0)	printf("Could not end utterance\n");

					//std::cout << std::endl;

					if (commandHandler)
					{
						log << identifiedWord.substr(0,2) << std::endl;
						log.flush();

						commandHandler->execute(identifiedWord.substr(0,2));
						//std::cout << std::endl;
					}

					rv = ps_start_utt(ps, "goforward");
					if (rv < 0) throw "Could not start utterance";

					ps_seg_free(iter);
					break;
				}
				iter = ps_seg_next(iter);
			}
#else
			std::string word;
			hyp = NULL;// ps_get_hyp(ps, &score, &uttid);
			if (hyp) word = hyp;

			if (word != "" && word != last_word)
			{
				counter = 1;
				last_word = word;
			}
			else
			{
				if (counter) counter++;//silencio apos word
			}

			if (counter > 1 && (counter * milliseconds > 100))
			{
				// End current utterante
				rv = ps_end_utt(ps);	// Estava falhando aqui e tive que gambiarrar!
				if (rv >= 0)
				{
					if (commandHandler)
					{
						// TODO quebrar string em palavras. Em Java seria apenas um: split(" ");
						commandHandler->execute(last_word);
					}

					// Start new utterance
					rv = ps_start_utt(ps, "goforward");
					if (rv < 0) throw "Could not start utterance";

					counter = 0;
				}
				else {
					printf("Could not end utterance\n");
					//if (counter > 1) counter--;
				}
			}
#endif
			// Libera buffer para gravacao
			sb.available = true;

			// Calcula delay
			long delay = GetTickCount() - sb.timestamp;
			delayAccum += delay;
			numBufProcessed++;
		}
		else Sleep(1);
	}

	log << "--" << std::endl;
	log.flush();

	std::cout	<< "Recognizer finished! " << std::endl
				<< "avg(Recognizer.DelayTime) = " << (delayAccum/numBufProcessed)
				<< " ms (Atraso de processamento de cada buffer apenas)" << std::endl
				<< std::endl;

    ps_free(ps);

	return 0;
}
